////////////////////////////////////////////////////////////////////////////
//
//  Crytek Engine Source File.
//  Copyright (C), Crytek Studios, 2001-2005.
// -------------------------------------------------------------------------
//  File name:   FacialInstance.h
//  Version:     v1.00
//  Created:     18/10/2005 by Timur.
//  Compilers:   Visual Studio.NET 2003
//  Description: 
// -------------------------------------------------------------------------
//  History:
//
////////////////////////////////////////////////////////////////////////////

#ifndef __FacialInstance_h__
#define __FacialInstance_h__
#pragma once

#include "FaceEffector.h"
#include "FaceState.h"
#include "FaceAnimation.h"
#include "EyeMovementFaceAnim.h"
#include "FacialModel.h"
class CCharInstance;

#if USE_FACIAL_ANIMATION_FRAMERATE_LIMITING
// Helper class to maintain a constant target frame rate
class CConstantFrameRateSkipper
{
	enum { SECOND_SLICES = 5 };
public:
	CConstantFrameRateSkipper() :
				m_currFrame(0),
				m_frameSkip(1),
				m_targetFrameRate(0),
				m_lastSecondSliceUpdates(0)
	{
		m_lastTime = gEnv->pTimer->GetAsyncCurTime();
	}
	void SetTargetFramerate(uint32 frameRate)
	{
		m_targetFrameRate = frameRate;
	}
	bool UpdateDue()
	{
		CTimeValue currTime = gEnv->pTimer->GetAsyncCurTime();
		f32 timeDelta = (currTime-m_lastTime).GetSeconds();

		if (timeDelta > 1.0f / SECOND_SLICES)
		{
			uint32 fps = m_lastSecondSliceUpdates * SECOND_SLICES;

			if (m_targetFrameRate < fps)
				m_frameSkip++;
			if (m_targetFrameRate > fps)
				m_frameSkip--;

			if (m_frameSkip <= 1)
				m_frameSkip = 1;

			m_lastSecondSliceUpdates = 0;
			m_lastTime = currTime;
		}

		m_currFrame++;
		if ((m_currFrame % m_frameSkip) == 0)
		{
			m_lastSecondSliceUpdates++;
			return true;
		}
		return false;
	}

	void GetMemoryUsage( ICrySizer *pSizer ) const
	{
		pSizer->AddObject( this, sizeof(*this) );
	}
	
private:
	uint32 m_targetFrameRate;
	uint32 m_currFrame;
	uint32 m_lastSecondSliceUpdates;
	uint32 m_frameSkip;
	CTimeValue m_lastTime;
};
#endif

//////////////////////////////////////////////////////////////////////////
// Instance of facial animation model.
//////////////////////////////////////////////////////////////////////////
class CFacialInstance : public IFacialInstance, public _i_reference_target_t
{
public:
	CFacialInstance( CFacialModel* pModel, CSkinInstance* pChar,CCharInstance* pMaster );

	//////////////////////////////////////////////////////////////////////////
	// IFacialInstance interface
	//////////////////////////////////////////////////////////////////////////
	VIRTUAL IFacialModel* GetFacialModel();
	VIRTUAL IFaceState*   GetFaceState();

	VIRTUAL uint32 StartEffectorChannel(IFacialEffector* pEffector,float fWeight,float fFadeTime,float fLifeTime=0,int nRepeatCount=0);
	VIRTUAL void StopEffectorChannel(uint32 nChannelID, float fFadeOutTime);
	VIRTUAL void PreviewEffector( IFacialEffector *pEffector,float fWeight,float fBalance=0.0f );
	VIRTUAL void PreviewEffectors( IFacialEffector **pEffectors,float *fWeights,float *fBalances,int nEffectorsCount );

	VIRTUAL IFacialAnimSequence *LoadSequence( const char *sSequenceName, bool addToCache=true );
	VIRTUAL void PrecacheFacialExpression(const char *sSequenceName);
	VIRTUAL void PlaySequence( IFacialAnimSequence *pSequence,EFacialSequenceLayer layer,bool bExclusive=false,bool bLooping=false );
	VIRTUAL void StopSequence( EFacialSequenceLayer layer );
	VIRTUAL bool IsPlaySequence( IFacialAnimSequence *pSequence, EFacialSequenceLayer layer );
	VIRTUAL void PauseSequence( EFacialSequenceLayer layer, bool bPaused );
	VIRTUAL void SeekSequence( EFacialSequenceLayer layer,float fTime );
	VIRTUAL void LipSyncWithSound( uint32 nSoundId, bool bStop=false );
	VIRTUAL void OnExpressionLibraryLoad();

	VIRTUAL void EnableProceduralFacialAnimation( bool bEnable );

	VIRTUAL void SetForcedRotations(int numForcedRotations, CFacialAnimForcedRotationEntry* forcedRotations);

	VIRTUAL void SetMasterCharacter(ICharacterInstance* pMasterInstance);
	VIRTUAL void TemporarilyEnableBoneRotationSmoothing();
	//////////////////////////////////////////////////////////////////////////

	void StopAllSequences();

	CFacialModel* GetModel() const { return m_pModel; }
	CFaceState* GetState() { return m_pFaceState; }
	CSkinInstance* GetCharacter() const { return m_pCharacter; }
	CCharInstance* GetMasterCharacter() const { return m_pMasterInstance; }

	CFacialAnimationContext* GetAnimContext() { return m_pAnimContext; }
	IFacialEffector* FindEffector( CFaceIdentifierHandle ident );
	IFacialEffector* FindEffector( const char* ident );

	// Called every time facial animation needs to be recalculated.
	void UpdatePlayingSequences( float fDeltaTimeSec, const QuatTS& rAnimLocationNext );
	void ProcessWaitingLipSync();
	void Update(CFacialDisplaceInfo& pInfo, float fDeltaTimeSec, const QuatTS& rAnimLocationNext);

#if USE_FACIAL_ANIMATION_FRAMERATE_LIMITING
	void SetTargetFramerate( uint32 fps ) { m_frameSkipper.SetTargetFramerate(fps); }
#endif
	
	void ApplyProceduralFaceBehaviour();
	void UpdateProceduralFaceBehaviour();
	void AdjustCamPosForProceduralFaceAnimation( Vec3 &vCamPos );

	uint32 SizeOfThis()
	{
		uint32 size = 0;
		size += sizeofVector(m_forcedRotations);
		if (m_pFaceState)
			size += m_pFaceState->SizeOfThis();

		return size;
	}

	void GetMemoryUsage( ICrySizer *pSizer ) const;
private:
	void Reset();
	void UpdateCurrentSequence(IFacialAnimSequence* pPreviousSequence);

	struct LayerInfo
	{
		LayerInfo(): sequence(0), bExclusive(false), bLooping(false) {}
		LayerInfo(IFacialAnimSequence* sequence, bool bExclusive, bool bLooping)
			: sequence(sequence), bExclusive(bExclusive), bLooping(bLooping) {}

		_smart_ptr<IFacialAnimSequence> sequence;
		bool bExclusive;
		bool bLooping;
		void GetMemoryUsage( ICrySizer* pSizer ) const
		{ 
			pSizer->AddObject( this, sizeof(*this) );
		};
	};

	LayerInfo m_layers[eFacialSequenceLayer_COUNT];
	int m_currentLayer;

#if USE_FACIAL_ANIMATION_FRAMERATE_LIMITING
	CConstantFrameRateSkipper m_frameSkipper;
#endif

	CSkinInstance *m_pCharacter; // Character for this face instance.
	CCharInstance* m_pMasterInstance; // == m_pCharacter, unless char is attachment, then master attachment.
	_smart_ptr<CFacialModel> m_pModel;

	_smart_ptr<CFaceState> m_pFaceState;
	_smart_ptr<CFacialAnimationContext> m_pAnimContext;
	_smart_ptr<CEyeMovementFaceAnim> m_pEyeMovement;

	class CLipSync 
	{
	public:
		CLipSync() : m_soundID(-1) {}
		void StartWaiting(IFacialAnimSequence* pSeq, int32 soundID) { m_pSequence = pSeq; m_soundID = soundID; }
		bool IsWaiting() const {	return ( m_soundID >= 0); };
		void Done() { m_soundID = -1; m_pSequence = NULL; };
		void GetMemoryUsage( ICrySizer* pSizer ) const
		{ 
			pSizer->AddObject( m_pSequence ); 
			pSizer->AddObject( m_soundID ); 
		};
		int32 GetSoundID() const { return m_soundID; } 
		IFacialAnimSequence* GetAnimSequence() const { return m_pSequence; }
	private:
		_smart_ptr<IFacialAnimSequence> m_pSequence;
		int32 m_soundID;
	};

	CLipSync m_waitingLipSync;

	//////////////////////////////////////////////////////////////////////////
	struct tProceduralFace
	{
		Vec3	m_JitterEyesPositions[4];
		int		m_nCurrEyesJitter;
		float	m_fEyesJitterScale;
		float	m_fLastJitterTime;
		float	m_fJitterTimeChange;
		float	m_fBlinkingTime;
		_smart_ptr<CFacialEffector> m_pBlink;
		bool	m_bEyesBlinking;
		bool	m_bEnabled;
		void GetMemoryUsage( ICrySizer* pSizer ) const
		{ 
			pSizer->AddObject( this, sizeof(*this) );
		};
	};
	tProceduralFace m_tProcFace;

	DynArray<CFacialAnimForcedRotationEntry> m_forcedRotations;
};


#endif // __FacialInstance_h__
